<?php

namespace liketp;

use ArrayAccess;
use Countable;
use liketp\model\Collection;
use liketp\model\concern\DbAction;

class Model implements ArrayAccess, Countable
{
    use DbAction;

    protected $prefix = '';
    protected $name = '';
    protected $table = '';
    protected $suffix = '';
    protected $pk = 'id';

    protected $isExists = false;

    protected $data = [];
    protected $origin = [];

    public function __construct($data = null)
    {
        $this->setName();
        if (is_array($data)) {
            $this->data($data);
            $this->origin = $data;
        }
    }

    public function setName(?string $name = null)
    {
        if (is_null($name)) {
            if (empty($this->name) == false) return;

            $className = trim(get_class($this), '\\');
            $name = substr($className, (strrpos($className, '\\') ?: -1) + 1);
            $this->name = str_to_snake($name);
        } else if (is_string($name)) {

            $this->name = $name;
        }
        return $this;
    }

    public function pk()
    {
        return $this->pk;
    }

    public function isExists(?bool $exists = null)
    {
        if (is_null($exists)) return $this->isExists;
        $this->isExists = $exists;
        return $exists;
    }

    public function data(?array $data = null)
    {
        if (is_null($data)) return $this->data;
        $this->data = $data;
        return $this;
    }

    public function origin(?string $fieldName = null)
    {
        if (is_null($fieldName)) return $this->origin;
        return $this->origin[$fieldName] ?? null;
    }

    public function copyDataToOrigin(bool $replace = false)
    {
        if ($replace) {
            $this->origin = $this->data;
        } else {
            $this->origin = array_merge($this->origin, $this->data);
        }
        return $this;
    }

    public function getAttr(string $attrName) {}

    public function toArray(bool $deep = false)
    {
        $data = $this->data;
        if ($deep) {
            foreach ($data as $k => $v) {
                if ($v instanceof Collection || $v instanceof self) {
                    $v = $v->toArray($deep);
                }
                $data[$k] = $v;
            }
        }
        return $data;
    }


    //------------------------------------------------ splice Query methods
    public function __call($method, $arguments)
    {
        return call_user_func_array([$this->db(), $method], $arguments);
    }

    public static function __callStatic($method, $arguments)
    {
        $model = new static();
        return call_user_func_array([$model->db(), $method], $arguments);
    }

    protected function db()
    {
        $query = App::instance()->db->connect();
        $query->model($this);

        if ($this->prefix) $query->prefix($this->prefix);
        if ($this->name) $query->name($this->name . $this->suffix);
        if ($this->table) $query->table($this->table);
        if ($this->pk) $query->pk($this->pk);

        return $query;
    }

    //------------------------------------------------ getter setter
    public function getData(string $key)
    {
        return $this->__get($key);
    }

    public function setData(string $key, $value)
    {
        return $this->__set($key, $value);
    }

    public function __get(string $key)
    {
        return $this->data[$key] ?? null;
    }

    public function __set(string $key, $value)
    {
        $this->data[$key] = $value;
    }

    //------------------------------------------------ ArrayAccess
    public function offsetExists($offset): bool
    {
        return isset($this->data[$offset]);
    }

    public function offsetGet($offset)
    {
        return $this->data[$offset] ?? null;
    }

    public function offsetSet($offset, $value): void
    {
        $this->data[$offset] = $value;
    }

    public function offsetUnset($offset): void
    {
        unset($this->data[$offset]);
    }

    //------------------------------------------------ Countable
    public function count(): int
    {
        return count($this->data);
    }
}
